package com.xplay.xpocker.room.mahjong.room;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.core.type.TypeReference;
import com.xplay.xpocker.config.MessageEvent;
import com.xplay.xpocker.config.security.CurrentUser;
import com.xplay.xpocker.constant.ConstantDict;
import com.xplay.xpocker.constant.MessageNoticeEnum;
import com.xplay.xpocker.constant.RoomTypeEnum;
import com.xplay.xpocker.constant.RoomTypeParent;
import com.xplay.xpocker.dvo.GameRuMahjongUserState;
import com.xplay.xpocker.dvo.AbstractRoom;
import com.xplay.xpocker.entity.*;
import com.xplay.xpocker.room.RoomStrategy;
import com.xplay.xpocker.room.mahjong.MahjongConfigProp;
import com.xplay.xpocker.room.mahjong.constant.MKey;
import com.xplay.xpocker.room.mahjong.dto.*;
import com.xplay.xpocker.room.message.MessageContent;
import com.xplay.xpocker.room.message.MessageToClient;
import com.xplay.xpocker.room.message.MessageType;
import com.xplay.xpocker.service.GameHiMahjongActionService;
import com.xplay.xpocker.service.GameRuMahjongFractionService;
import com.xplay.xpocker.service.GameRuMahjongLogsService;
import com.xplay.xpocker.service.gaming.*;
import com.xplay.xpocker.observer.ChannelHandlerManager;
import com.xplay.xpocker.util.Assertion;
import com.xplay.xpocker.util.CardUtil;
import com.xplay.xpocker.util.JacksonStringUtil;
import com.xplay.xpocker.util.ObjectUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class MahjongRoomHandler extends RoomStrategy<MahjongRoomRule, MahjongRoomInfo, GameRuMahjongLogs> {
    @Resource
    private GameRuRoomService gameRuRoomService;
    @Resource
    private GameRuRoomUserService roomUserService;
    @Autowired
    private GameRuMahjongService mahjongService;
    @Autowired
    private GameRuMahjongService gameRuMahjongService;
    @Autowired
    private GameRuMahjongUserService gameRuMahjongUserService;

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    @Resource
    private GameRuMahjongFractionService fractionService;

    @Resource
    private GameRuMahjongOpenDealService openDealService;
    @Resource
    private GameRuMahjongLogsService mahjongLogsService;


    @Autowired
    private GameHiMahjongActionService hiMahjongActionService;

    @Override
    protected int strategyType() {
        return RoomTypeParent.Mahjong;
    }

    @Override
    protected GameRuRoom doCreate(String code, String type, MahjongRoomRule wanZMahjongRoom) {
        //判断用户是否在房间里面
        GameRuRoomUser exitsRoom = roomUserService.addHome(CurrentUser.getUserId(), null);
        if (exitsRoom != null) {
            return gameRuRoomService.getById(exitsRoom.getRoomId());
        }
        //创建房间
        GameRuRoom roomInfo = gameRuRoomService.createRoom(code, type, JacksonStringUtil.objectToString(wanZMahjongRoom, false));
        //一些麻将运行时需要的数据初始化
        GameRuMahjongUser mahjongUser = new GameRuMahjongUser();
        mahjongUser.setUserId(CurrentUser.getUserId());
        mahjongUser.setRoomId(roomInfo.getId());
        mahjongUser.setBanker(true);
        gameRuMahjongUserService.createOrUpdate(mahjongUser);
        // 添加房间运行时数据比如 房间状态 房间桌面上的牌 开始时间结束时间等等
        GameRuMahjong ruMahjong = new GameRuMahjong();
        ruMahjong.setRoomId(roomInfo.getId());
        ruMahjong.setCode(roomInfo.getCode());
        RoomTypeEnum typeEnum = RoomTypeEnum.of(type);
        ruMahjong.setName(typeEnum.name);
        ruMahjong.setStatus(MahjongStatusEnum.CREATING.getStatus());
        ruMahjong.setCreateDate(new Date());
        ruMahjong.setCreater(CurrentUser.getUsername());
        ruMahjong.setCards(CardUtil.buildAllCardString());
        gameRuMahjongService.save(ruMahjong);
        return roomInfo;

    }


    @Override
    public void saveActionLog(List<MessageToClient> message, AbstractRoom<MahjongRoomRule, MahjongRoomInfo, GameRuMahjongLogs> roomInfo) {
        if (ObjectUtils.isEmpty(message)) {
            return;
        }
        List<MessageToClient> messageToClients = message;
        messageToClients = messageToClients.stream().filter(item -> {
            if (item.get(MKey.type) == null) {
                return false;
            }
            // 通知别人拿出交换信息   干掉
            if (item.get(MKey.type).equals(MahjongActionEnum.EXCHANGE.getAction()) && item.get(MKey.swapCard) == null) {
                return false;
                // 通知别人摸牌了   null  干掉
            } else if (item.get(MKey.type).equals(MahjongActionEnum.REFRESH_ACTION.getAction())) {
                return false;
                // 通知别人摸牌了   null  干掉
            } else
                return !item.get(MKey.type).equals(MahjongActionEnum.TOUCH.getAction()) || item.get(MKey.cardCode) != null;
        }).collect(Collectors.toList());
        if (ObjectUtils.isEmpty(messageToClients)) {
            return;
        }
        messageToClients.sort(Comparator.comparingDouble(s -> s.get(MKey.sleep) == null ? 0 : Double.parseDouble(s.get(MKey.sleep).toString())));
        String beforeType = null;
        for (MessageToClient messageItem : messageToClients) {
            if (beforeType == null || !beforeType.equals(messageItem.get(MKey.type).toString())) {
                beforeType = messageItem.get(MKey.type).toString();
            } else {
                continue;
            }
            if (beforeType.equals(MahjongActionEnum.READING.getAction())) {
                continue;
            }
            String finalBeforeType = beforeType;
            List<MessageToClient> saveInfo = messageToClients.stream().filter(item -> item.get(MKey.type).toString().equals(finalBeforeType)).collect(Collectors.toList());

            if (beforeType.equals(MahjongActionEnum.DICE.getAction())) {
                List<MessageToClient> newSaveInfo = new ArrayList<MessageToClient>();
                newSaveInfo.add(saveInfo.get(0));
                saveInfo = newSaveInfo;
            }
            saveList(saveInfo, roomInfo);
        }
    }

    protected void saveList(List<MessageToClient> message, AbstractRoom<MahjongRoomRule, MahjongRoomInfo, GameRuMahjongLogs> roomInfo) {
        GameHiMahjongAction hiAction = new GameHiMahjongAction();
        hiAction.setRoomId(roomInfo.getGameInfo().getRoomId());
        hiAction.setSyncVersion(roomInfo.getGameInfo().getVersion());
        hiAction.setCreateDate(new Date());
        hiAction.setAction(JacksonStringUtil.objectToString(message));
        hiAction.setRoomInfo(JacksonStringUtil.objectToString(roomInfo));
        hiAction.setSeveralInning(roomInfo.getSeveralInning());
        hiMahjongActionService.save(hiAction);
    }

    /**
     * 当用户进入房间
     *
     * @param roomCode
     */
    @Override
    protected AbstractRoom<MahjongRoomRule, MahjongRoomInfo, GameRuMahjongLogs> doAddRoom(String roomCode) {
        AbstractRoom<MahjongRoomRule, MahjongRoomInfo, GameRuMahjongLogs> roomInfo = currentLoad(roomCode, CurrentUser.getUserId());
        Assertion.isTrue(roomInfo.getGameInfo().getAllUser().size() == roomInfo.getRule().getUserCount() && roomInfo.getGameInfo().getAllUser().stream().noneMatch(element -> element.getUserId().equals(CurrentUser.getUserId())), "房间已满");
        GameRuRoomUser gameRuRoomUser = roomUserService.addHome(CurrentUser.getUserId(), roomInfo.getId());
        if (gameRuRoomUser.isOld()) {
            return roomInfo;
        }
        // 如果游戏还未开始  并且人数只有小于房间规则人数  这里只需要消息通知进入事件
        // 如果游戏还未开始  并且人数等于规则人数  这里就需要开始洗牌  并为每个人发送手牌
        // 如果支持换三张  请为每一个用户添加
        // 如果游戏已经开始  只做状态查询
        if (roomInfo.getStatus().equals(MahjongStatusEnum.CREATING.getStatus())) {
            GameRuMahjongUser mahjongUser = new GameRuMahjongUser();
            mahjongUser.setUserId(CurrentUser.getUserId());
            mahjongUser.setRoomId(roomInfo.getId());
            mahjongUser.setWaitCompleted(MahjongActionEnum.READING.getAction());
            mahjongUser.setBanker(false);
            gameRuMahjongUserService.createOrUpdate(mahjongUser);
        }
        // 通知房间更新
        MessageContent<MahjongMessage> messageContent = new MessageContent<>(MessageType.ACTION.getType(), MahjongMessage.Builder.builder().type(MahjongActionEnum.REFRESH.getAction()).build());
        messageContent.setRoomCode(roomInfo.getGameInfo().getCode());
        messageContent.setMnEnum(MessageNoticeEnum.ALL);
        applicationEventPublisher.publishEvent(new MessageEvent(this, messageContent));

        return roomInfo;
    }

    /**
     * 这里需要加载房间所有信息
     * 当用户断线重连，需要读取这个信息
     *
     * @param roomCode
     * @return
     */

    @Override
    protected AbstractRoom<MahjongRoomRule, MahjongRoomInfo, GameRuMahjongLogs> load(String roomCode) {
        return mahjongService.queryRoomInfo(roomCode);
    }

    @Override
    public AbstractRoom<MahjongRoomRule, MahjongRoomInfo, GameRuMahjongLogs> currentLoad(String roomCode, BigDecimal userId) {
        AbstractRoom<MahjongRoomRule, MahjongRoomInfo, GameRuMahjongLogs> result = load(roomCode);
        Assertion.isNull(result,"房间信息为空,请检查");
        Assertion.isNull(result.getStatus(),"房间状态为空,请检查");
        if (!result.getStatus().equals(MahjongStatusEnum.BALANCE.getStatus()) && MahjongConfigProp.getInstance().isDesensitize()) {
            result = desensitize(result, result.getCode(), userId);
        }
        return result;
    }

    /**
     * @param roomInfo 当前类已经又 load 了 为什么还要传入一个room   是因为避免没有持久化的房间
     * @param roomCode 如果没有传room 从roomCode 再次加载
     * @param userId   用户ID
     * @return
     */

    @Override
    public AbstractRoom<MahjongRoomRule, MahjongRoomInfo, GameRuMahjongLogs> desensitize(AbstractRoom<MahjongRoomRule, MahjongRoomInfo, GameRuMahjongLogs> roomInfo, String roomCode, BigDecimal userId) {
        if (roomInfo == null) {
            roomInfo = load(roomCode);
        }
        if (!MahjongConfigProp.getInstance().isDesensitize()) {
            return roomInfo;
        }
        String serializeRoom = JacksonStringUtil.objectToString(roomInfo, false);
        AbstractRoom<MahjongRoomRule, MahjongRoomInfo, GameRuMahjongLogs> copyObj = JacksonStringUtil.objectCase(serializeRoom, new TypeReference<AbstractRoom<MahjongRoomRule, MahjongRoomInfo, GameRuMahjongLogs>>() {
        });
        if (copyObj == null || copyObj.getGameInfo() == null) {
            return null;
        }
        copyObj.setLogs(null);
        List<GameRuMahjongUserState> otherUsers = new ArrayList<>();
        /**
         * 将存储在其它地方的用户信息取出来
         * allUser     桌面上所有用户
         * otherUsers  桌面上其它用户
         * currentUser 当前用户
         */
        if (ObjectUtils.notEmpty(copyObj.getGameInfo().getAllUser())) {
            otherUsers.addAll(copyObj.getGameInfo().getAllUser().stream().filter(item -> !item.getUserId().equals(userId)).collect(Collectors.toList()));
        }
        if (ObjectUtils.notEmpty(copyObj.getGameInfo().getOtherUsers())) {
            otherUsers.addAll(copyObj.getGameInfo().getOtherUsers().stream().filter(item -> !item.getUserId().equals(userId)).collect(Collectors.toList()));
        }
        if (copyObj.getGameInfo().getCurrentUser() != null && !copyObj.getGameInfo().getCurrentUser().getUserId().equals(userId)) {
            otherUsers.add(copyObj.getGameInfo().getCurrentUser());
        }
        /**
         * 循环
         * 核心信息过滤 隐藏
         */
        for (GameRuMahjongUserState userInfo : otherUsers) {
            if (ObjectUtils.isEmpty(userInfo.getHandCards())) {
                userInfo.setHandCardCount(0);
            } else {
                int cardCount = userInfo.getHandCards().split(ConstantDict.Separator.comma).length;
                if (userInfo.getTouchCard() != null) {
                    cardCount++;
                    // 屏蔽摸牌信息
                    userInfo.setTouchCard(null);
                }
                userInfo.setHandCardCount(cardCount);
                // 屏蔽手牌信息
                userInfo.setHandCards(null);
            }
            userInfo.setSwapCardIn(null);
            userInfo.setSwapCardOut(null);
        }
        return copyObj;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void exitRoom(GameRuRoom room, BigDecimal userId) {
        Assertion.isTrue(!room.getStatus().equals(MahjongStatusEnum.CREATING.getStatus()) && !room.getUserId().equals(userId), "游戏已开始，请申请解散");
        if (room.getUserId().equals(userId)) {
            // 创建人退出
            cleanRoom(room);
        } else {
            roomUserService.exitHome(userId, room.getId());
            gameRuMahjongUserService.remove(Wrappers.<GameRuMahjongUser>lambdaQuery().eq(GameRuMahjongUser::getUserId, userId));
        }
        // 通知房间更新
        MessageContent<MahjongMessage> messageContent = new MessageContent<>(MessageType.ACTION.getType(), MahjongMessage.Builder.builder().type(MahjongActionEnum.REFRESH.getAction()).build());
        messageContent.setRoomCode(room.getCode());
        messageContent.setMnEnum(MessageNoticeEnum.ALL);
        applicationEventPublisher.publishEvent(new MessageEvent(this, messageContent));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cleanRoom(GameRuRoom room) {
        // 清理掉运行时日志  记录服务器下发牌   以及 用户行为   用户上报行为
        mahjongLogsService.remove(Wrappers.<GameRuMahjongLogs>lambdaQuery().eq(GameRuMahjongLogs::getRoomId, room.getId()));
        // 删除运行时房间信息
        gameRuMahjongService.remove(Wrappers.<GameRuMahjong>lambdaQuery().eq(GameRuMahjong::getRoomId, room.getId()));
        // 删除运行时分数信息
        fractionService.remove(Wrappers.<GameRuMahjongFraction>lambdaQuery().eq(GameRuMahjongFraction::getRoomId, room.getId()));
        // 删除碰撞信息
        openDealService.remove(Wrappers.<GameRuMahjongOpenDeal>lambdaQuery().eq(GameRuMahjongOpenDeal::getRoomId, room.getId()));
        // 删除 用户在房间的信息
        gameRuMahjongUserService.remove(Wrappers.<GameRuMahjongUser>lambdaQuery().eq(GameRuMahjongUser::getRoomId, room.getId()));
        // 删除运行时房间内容
        gameRuRoomService.remove(Wrappers.<GameRuRoom>lambdaQuery().eq(GameRuRoom::getId, room.getId()));
        // 删除运行时房间信息
        roomUserService.remove(Wrappers.<GameRuRoomUser>lambdaQuery().eq(GameRuRoomUser::getRoomId, room.getId()));
        // 清理在线信息
        ChannelHandlerManager.dissolution(room.getCode());
    }

}
